/***************************************************************************//**
 * @file CGI/AirportLights.cpp
 * @author  Marek M. Cel <marekcel@mscsim.org>
 *
 * @section LICENSE
 *
 * Copyright (C) 2014 Marek M. Cel
 *
 * This file is part of MScSim. You can redistribute and modify it under
 * the terms of GNU General Public License as published by the Free Software
 * Foundation; either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Further information about the GNU General Public License can also be found
 * on the world wide web at http://www.gnu.org.
 ******************************************************************************/
#ifndef AIRPORT_LIGHTS_CPP
#define AIRPORT_LIGHTS_CPP
#endif

////////////////////////////////////////////////////////////////////////////////

#include <iostream>

#include <osg/Texture2D>

#include <osgDB/ReadFile>

#include <osgSim/LightPoint>
#include <osgSim/Sector>

#include <CGI/Airport.h>
#include <CGI/AirportLights.h>

////////////////////////////////////////////////////////////////////////////////

using namespace std;

////////////////////////////////////////////////////////////////////////////////

AirportLights::AirportLights( CGI_Data *data ) :
    m_data ( data )
{
    m_airportLightsGroup = new osg::Group();
    m_airportLightsGroup->setName( "AirportLightsGroup" );

    createLights();
}

////////////////////////////////////////////////////////////////////////////////

void AirportLights::createLights()
{
    osg::ref_ptr<osgSim::LightPointNode> lightPointsNode = new osgSim::LightPointNode();

    createRunwayEdgeLights( lightPointsNode.get() );
    createRunwayFarEndLights( lightPointsNode.get() );
    createPAPI( lightPointsNode.get() );
    createALSF2( lightPointsNode.get() );

    // texture
    std::string texFile = "data/textures/various/lightpoint.png";
    osg::ref_ptr<osg::Image> img = osgDB::readImageFile( texFile );

    if ( img.valid() )
    {
        osg::ref_ptr<osg::StateSet> set = lightPointsNode->getOrCreateStateSet();

        lightPointsNode->setPointSprite();

        osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D();
        tex->setImage( img.get() );

        set->setTextureAttributeAndModes( 0, tex.get(), osg::StateAttribute::ON );
    }
    else
    {
        cerr << "ERROR! Cannot open texture file: " << texFile << endl;
    }

    m_airportLightsGroup->addChild( lightPointsNode.get() );
}

////////////////////////////////////////////////////////////////////////////////

void AirportLights::createRunwayEdgeLights( osgSim::LightPointNode *lightPointNode )
{
    double xcur = -1850.0;

    while ( xcur <= 1850.0 + 1.0e-4 )
    {
        osgSim::LightPoint lighPntL( osg::Vec3( xcur, -Airport::m_halfWidth - 5.5, -1.0 ), osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );
        osgSim::LightPoint lighPntR( osg::Vec3( xcur,  Airport::m_halfWidth + 5.5, -1.0 ), osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );

        lighPntL._intensity = 5.0;
        lighPntR._intensity = 5.0;
        lighPntL._radius = 0.5;
        lighPntR._radius = 0.5;

        bool addR = true;

        if ( xcur < -1800 ) addR = false;
        else if ( xcur > -800.0 && xcur < -750.0) addR = false;
        else if ( xcur <  800.0 && xcur >  750.0) addR = false;
        else if ( xcur >  1800 ) addR = false;

        lightPointNode->addLightPoint( lighPntL );
        if ( addR ) lightPointNode->addLightPoint( lighPntR );

        xcur += 15.0;
    }
}

////////////////////////////////////////////////////////////////////////////////

void AirportLights::createRunwayFarEndLights( osgSim::LightPointNode *lightPointNode )
{
    double xcur = 0.0;
    double ycur = 0.0;

    xcur = -1850.0;

    for ( int iLon = 0; iLon < 2; iLon++ )
    {
        float sectorDegFrom = 0.0f;
        float sectorDegUnto = 0.0f;

        if ( xcur < 0.0 )
        {
            sectorDegFrom = osg::inDegrees(   0.0 );
            sectorDegUnto = osg::inDegrees( 180.0 );
        }
        else
        {
            sectorDegFrom = osg::inDegrees( 180.0 );
            sectorDegUnto = osg::inDegrees( 360.0 );
        }

        osg::ref_ptr<osgSim::Sector> sector = new osgSim::AzimSector( sectorDegFrom, sectorDegUnto );

        ycur = -25.0;

        for ( int iLat = 0; iLat < 21; iLat++ )
        {
            osgSim::LightPoint lighPnt( osg::Vec3( xcur, ycur, -1.0 ), osg::Vec4( 1.0, 0.0, 0.0, 1.0 ) );

            lighPnt._sector = sector;

            lighPnt._intensity = 5.0;
            lighPnt._radius = 0.5;

            lightPointNode->addLightPoint( lighPnt );

            ycur += 2.5;
        }

        xcur = 1850.0;
    }
}

////////////////////////////////////////////////////////////////////////////////

void AirportLights::createPAPI( osgSim::LightPointNode *lightPointNode )
{
    double xcur = 0.0;

    double intensity = 5.0;
    double radius = 1.0;

    xcur = -1525.0;

    for ( int iLon = 0; iLon < 2; iLon++ )
    {
        float sectorDegFrom = 0.0f;
        float sectorDegUnto = 0.0f;

        double ya = 0.0;
        double yb = 0.0;
        double yc = 0.0;
        double yd = 0.0;

        if ( xcur < 0.0 )
        {
            sectorDegFrom = osg::inDegrees( 180.0 );
            sectorDegUnto = osg::inDegrees( 360.0 );

            ya = -70.0;
            yb = -60.0;
            yc = -50.0;
            yd = -40.0;
        }
        else
        {
            sectorDegFrom = osg::inDegrees(   0.0 );
            sectorDegUnto = osg::inDegrees( 180.0 );

            ya = 70.0;
            yb = 60.0;
            yc = 50.0;
            yd = 40.0;
        }

        osg::ref_ptr<osgSim::Sector> sectorAR =
                new osgSim::AzimElevationSector( sectorDegFrom, sectorDegUnto,
                                                 osg::inDegrees(   0.0 ), osg::inDegrees(  -2.5 ) );
        osg::ref_ptr<osgSim::Sector> sectorAW =
                new osgSim::AzimElevationSector( sectorDegFrom, sectorDegUnto,
                                                 osg::inDegrees(  -2.5 ), osg::inDegrees( -90.0 ) );
        osg::ref_ptr<osgSim::Sector> sectorBR =
                new osgSim::AzimElevationSector( sectorDegFrom, sectorDegUnto,
                                                 osg::inDegrees(   0.0 ), osg::inDegrees(  -2.8 ) );
        osg::ref_ptr<osgSim::Sector> sectorBW =
                new osgSim::AzimElevationSector( sectorDegFrom, sectorDegUnto,
                                                 osg::inDegrees(  -2.8 ), osg::inDegrees( -90.0 ) );
        osg::ref_ptr<osgSim::Sector> sectorCR =
                new osgSim::AzimElevationSector( sectorDegFrom, sectorDegUnto,
                                                 osg::inDegrees(   0.0 ), osg::inDegrees(  -3.2 ) );
        osg::ref_ptr<osgSim::Sector> sectorCW =
                new osgSim::AzimElevationSector( sectorDegFrom, sectorDegUnto,
                                                 osg::inDegrees(  -3.2 ), osg::inDegrees( -90.0 ) );
        osg::ref_ptr<osgSim::Sector> sectorDR =
                new osgSim::AzimElevationSector( sectorDegFrom, sectorDegUnto,
                                                 osg::inDegrees(   0.0 ), osg::inDegrees(  -3.5 ) );
        osg::ref_ptr<osgSim::Sector> sectorDW =
                new osgSim::AzimElevationSector( sectorDegFrom, sectorDegUnto,
                                                 osg::inDegrees(  -3.5 ), osg::inDegrees( -90.0 ) );

        osgSim::LightPoint lighPntAR( osg::Vec3( xcur, ya, -2.5 ), osg::Vec4( 1.0, 0.0, 0.0, 1.0 ) );
        osgSim::LightPoint lighPntAW( osg::Vec3( xcur, ya, -2.5 ), osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );
        osgSim::LightPoint lighPntBR( osg::Vec3( xcur, yb, -2.5 ), osg::Vec4( 1.0, 0.0, 0.0, 1.0 ) );
        osgSim::LightPoint lighPntBW( osg::Vec3( xcur, yb, -2.5 ), osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );
        osgSim::LightPoint lighPntCR( osg::Vec3( xcur, yc, -2.5 ), osg::Vec4( 1.0, 0.0, 0.0, 1.0 ) );
        osgSim::LightPoint lighPntCW( osg::Vec3( xcur, yc, -2.5 ), osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );
        osgSim::LightPoint lighPntDR( osg::Vec3( xcur, yd, -2.5 ), osg::Vec4( 1.0, 0.0, 0.0, 1.0 ) );
        osgSim::LightPoint lighPntDW( osg::Vec3( xcur, yd, -2.5 ), osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );

        lighPntAR._intensity = intensity;
        lighPntAW._intensity = intensity;
        lighPntBR._intensity = intensity;
        lighPntBW._intensity = intensity;
        lighPntCR._intensity = intensity;
        lighPntCW._intensity = intensity;
        lighPntDR._intensity = intensity;
        lighPntDW._intensity = intensity;

        lighPntAR._radius = radius;
        lighPntAW._radius = radius;
        lighPntBR._radius = radius;
        lighPntBW._radius = radius;
        lighPntCR._radius = radius;
        lighPntCW._radius = radius;
        lighPntDR._radius = radius;
        lighPntDW._radius = radius;

        lighPntAR._sector = sectorAR;
        lighPntAW._sector = sectorAW;
        lighPntBR._sector = sectorBR;
        lighPntBW._sector = sectorBW;
        lighPntCR._sector = sectorCR;
        lighPntCW._sector = sectorCW;
        lighPntDR._sector = sectorDR;
        lighPntDW._sector = sectorDW;

        lightPointNode->addLightPoint( lighPntAR );
        lightPointNode->addLightPoint( lighPntAW );
        lightPointNode->addLightPoint( lighPntBR );
        lightPointNode->addLightPoint( lighPntBW );
        lightPointNode->addLightPoint( lighPntCR );
        lightPointNode->addLightPoint( lighPntCW );
        lightPointNode->addLightPoint( lighPntDR );
        lightPointNode->addLightPoint( lighPntDW );

        xcur = 1525.0;
    }
}


////////////////////////////////////////////////////////////////////////////////

void AirportLights::createALSF2( osgSim::LightPointNode *lightPointNode )
{
    double xcur = 0.0;
    double ycur = 0.0;

    double xdel = 0.0;
    double ydel = 0.0;

    double step = 0.0;

    int iFlash = 0;

    xcur = -1851.0;

    for ( int iLon = 0; iLon < 2; iLon++ )
    {
        iFlash = 0;

        float sectorDegFrom = 0.0f;
        float sectorDegUnto = 0.0f;

        if ( xcur < 0.0 )
        {
            sectorDegFrom = osg::inDegrees( 180.0 );
            sectorDegUnto = osg::inDegrees( 360.0 );
        }
        else
        {
            sectorDegFrom = osg::inDegrees(   0.0 );
            sectorDegUnto = osg::inDegrees( 180.0 );
        }

        osg::ref_ptr<osgSim::Sector> sector = new osgSim::AzimSector( sectorDegFrom, sectorDegUnto );

        ycur =   0.0;
        ydel = -30.0;

        // runway treshold lights
        for ( int iLat = 0; iLat < 21; iLat++ )
        {
            osgSim::LightPoint lighPnt( osg::Vec3( xcur, ycur + ydel, -1.0 ), osg::Vec4( 0.0, 1.0, 0.0, 1.0 ) );

            lighPnt._sector = sector;

            lighPnt._intensity = 5.0;
            lighPnt._radius = 0.5;

            lightPointNode->addLightPoint( lighPnt );

            ydel += 3.0;
        }

        //
        if ( xcur < 0.0 )
        {
            step = -30.0;
            xdel = -30.0;
        }
        else
        {
            step =  30.0;
            xdel =  30.0;
        }

        while ( fabs( xdel ) <= 750.0 + 1.0e-4 )
        {
            ydel = -2.0;

            for ( int iLat = 0; iLat < 5; iLat++ )
            {
                osgSim::LightPoint lighPnt( osg::Vec3( xcur + xdel, ydel, -3.0 ), osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );

                lighPnt._intensity = 5.0;
                lighPnt._radius = 0.35;

                lighPnt._sector = sector;

                lightPointNode->addLightPoint( lighPnt );

                ydel += 1.0;
            }

            if( fabs( fabs( xdel ) - 150.0 ) < 1.0e-4 )
            {
                ycur = -7.5;

                for ( int iSide = 0; iSide < 2; iSide++ )
                {
                    ydel = -2.25;

                    for ( int iLat = 0; iLat < 4; iLat++ )
                    {
                        osgSim::LightPoint lighPnt( osg::Vec3( xcur + xdel, ycur + ydel, -3.0 ), osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );

                        lighPnt._intensity = 5.0;
                        lighPnt._radius = 0.35;

                        lighPnt._sector = sector;

                        lightPointNode->addLightPoint( lighPnt );

                        ydel += 1.5;
                    }

                    ycur = 7.5;
                }
            }

            if( fabs( fabs( xdel ) - 300.0 ) < 1.0e-4 )
            {
                ycur = -12.5;

                for ( int iSide = 0; iSide < 2; iSide++ )
                {
                    ydel = -6.0;

                    for ( int iLat = 0; iLat < 9; iLat++ )
                    {
                        osgSim::LightPoint lighPnt( osg::Vec3( xcur + xdel, ycur + ydel, -3.0 ), osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );

                        lighPnt._intensity = 5.0;
                        lighPnt._radius = 0.35;

                        lighPnt._sector = sector;

                        lightPointNode->addLightPoint( lighPnt );

                        ydel += 1.5;
                    }

                    ycur = 12.5;
                }
            }

            if ( fabs( xdel ) < 300.0 )
            {
                ycur = -15.0;

                for ( int iSide = 0; iSide < 2; iSide++ )
                {
                    ydel = -2.0;

                    for ( int iLat = 0; iLat < 3; iLat++ )
                    {
                        osgSim::LightPoint lighPnt( osg::Vec3( xcur + xdel, ycur + ydel, -3.0 ), osg::Vec4( 1.0, 0.0, 0.0, 1.0 ) );

                        lighPnt._intensity = 5.0;
                        lighPnt._radius = 0.35;

                        lighPnt._sector = sector;

                        lightPointNode->addLightPoint( lighPnt );

                        ydel += 2.0;
                    }

                    ycur = 15.0;
                }

            }
            else
            {
                osg::ref_ptr<osgSim::BlinkSequence> blinkSequence = new osgSim::BlinkSequence;

                for ( int iBlink = 16; iBlink >= 0; iBlink-- )
                {
                    float alpha = ( iFlash == iBlink ) ? 1.0 : 0.0;
                    blinkSequence->addPulse( 0.05, osg::Vec4( 1.0, 1.0, 1.0, alpha ) );
                }

                osgSim::LightPoint lighPnt( osg::Vec3( xcur + xdel, 0.0, -3.0 ), osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );

                lighPnt._intensity = 5.0;
                lighPnt._radius = 0.5;

                lighPnt._sector = sector;

                lighPnt._blinkSequence = blinkSequence;

                lightPointNode->addLightPoint( lighPnt );

                iFlash++;
            }

            xdel += step;
        }

        xcur = 1851.0;
    }
}
